﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ZLGAPI;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Rebar;

namespace Bootloader.zlgcan
{
    public class ZlgCanDevice
    {
        public enum CanSendMode
        {
            CAN = 0,
            CANFD = 1,
            CANFD_BRS = 2,
        }

        private readonly uint _deviceType = ZLGCAN.ZCAN_USBCANFD_200U;// 设备类型
        private readonly uint _deviceIndex = 0;// 设备索引号（存在多个设备时会用到）
        private readonly uint _deviceChannel = 0;// 设备通道

        private ZlgCanDevice.CanSendMode _isCanFDSend = ZlgCanDevice.CanSendMode.CAN;// CAN发送模式
        private IntPtr _deviceHandle = IntPtr.Zero;// 设备句柄
        private IntPtr _chnHandle = IntPtr.Zero;  // 通道句柄
        private CancellationTokenSource _zlgRevThreadCancelToken;// zlg接收监听线程是否结束运行
        Thread _zlgRevListenThread;// zlg后台监听线程句柄
        private CanFilter _canFilter;// CANID过滤器
        private Action<uint, byte[]> _onReceiveCall = null;// 将数据传给消费者，外部回调函数

        // zlg线程传参用
        class ZlgThreadParams
        {
            public CancellationToken CancellationToken { get; set; }//是否结束运行
            public IntPtr deviceHandle { get; set; }     // 设备句柄
            public IntPtr chnHandle { get; set; }     // 通道句柄
            public int chnNum { get; set; }     // 通道号
        }

        // 构造方法：传入要打开的设备、通道和工作模式
        public ZlgCanDevice(uint deviceType, uint deviceIndex, uint deviceChannel)
        {
            _deviceType = deviceType;
            _deviceIndex = deviceIndex;
            _deviceChannel = deviceChannel;
        }

        // 默认打开USBCAN 0 0
        public ZlgCanDevice()
        {

        }

        // 打开设备
        public bool OpenDevice()
        {
            // zlg上次异常关闭会导致下次获取到假句柄，所以打开前先彻底关闭
            bool ok = this.CloseDevice();
            if (!ok)
            {
                return false;
            }

            // 打开设备
            _deviceHandle = ZLGAPI.ZLGCAN.ZCAN_OpenDevice(_deviceType, _deviceIndex, 0U);
            if (_deviceHandle == IntPtr.Zero)
            {
                // 打开失败
                return false;
            }

            // 初始化通道
            _chnHandle = Init_chn_USBCANFD(_deviceHandle, _deviceChannel);
            if (_chnHandle == IntPtr.Zero)
            {
                // 打开失败
                return false;
            }

            // 监听通道
            _zlgRevThreadCancelToken = new CancellationTokenSource();
            ZlgThreadParams zlgThreadParams = new ZlgThreadParams
            {
                CancellationToken = _zlgRevThreadCancelToken.Token,
                chnHandle = _chnHandle
            };
            _zlgRevListenThread = new Thread(this.ZlgReceiveThread);   // 每通道独立线程接收
            _zlgRevListenThread.IsBackground = true;                   // 设置为后台线程，不需要join
            _zlgRevListenThread.Start(zlgThreadParams);                // 启动线程

            return true;
        }

        // 关闭设备
        public bool CloseDevice()
        {
            // 句柄为空返回关闭成功
            if (_deviceHandle == IntPtr.Zero)
            {
                return true;
            }

            // 停止接收监听线程
            _zlgRevThreadCancelToken?.Cancel();

            if (_zlgRevListenThread != null && _zlgRevListenThread.IsAlive)
            {
                if (!_zlgRevListenThread.Join(TimeSpan.FromSeconds(1))) // 最多等1秒
                {
                    Debug.WriteLine("接收线程未在1秒内退出，强制继续...");
                }
                _zlgRevListenThread = null;
            }

            // 通道句柄不为空，关闭通道
            if (_chnHandle != IntPtr.Zero)
            {
                uint ok = ZLGCAN.ZCAN_ResetCAN(_chnHandle);
                if (ok != 1)
                {
                    Debug.WriteLine("通道关闭失败");
                }
            }
            _chnHandle = IntPtr.Zero;// 释放通道句柄

            // 关闭设备
            uint ret = ZLGAPI.ZLGCAN.ZCAN_CloseDevice(_deviceHandle);
            if (ret != 1)
            {
                Debug.WriteLine("设备失败");
                return false;// 关闭失败
            }

            _deviceHandle = IntPtr.Zero;// 释放设备句柄
            return true;
        }

        // 配置ID过滤器
        public void SetCanFilter(List<uint> idList)
        {
            _canFilter = new CanFilter(idList);
        }

        // 设置CAN/CANFD发送
        public void SetCanfdMode(CanSendMode canMode)
        {
            _isCanFDSend = canMode;
        }

        // 发送CAN/CANFD
        public bool SendFrame(uint id, byte[] data)
        {
            // CANFD发送
            if (_isCanFDSend == ZlgCanDevice.CanSendMode.CANFD_BRS || _isCanFDSend == ZlgCanDevice.CanSendMode.CANFD)
            {
                return ZlgCANFD_Transmit(id, data);
            }
            // CAN发送
            else
            {
                return ZlgCAN_Transmit(id, data);
            }
        }

        // 由消费者提供该函数的实现
        public void SetReceiveCallback(Action<uint, byte[]> callback)
        {
            _onReceiveCall = callback;
        }

        // 初始化CAN通道
        private IntPtr Init_chn_USBCANFD(IntPtr dev, uint chn_idx)
        {
            IntPtr temp;
            uint ret;           // 返回值

            // 仲裁域波特率
            string path = String.Format("{0}/canfd_abit_baud_rate", chn_idx);
            ret = ZLGCAN.ZCAN_SetValue(dev, path, "500000");       // 500k
            if (ret != 1)
            {
                Debug.WriteLine("设置仲裁域波特率失败");
                return IntPtr.Zero;
            }

            // 数据域波特率
            path = String.Format("{0}/canfd_dbit_baud_rate", chn_idx);
            ret = ZLGCAN.ZCAN_SetValue(dev, path, "2000000");      // 2M
            if (ret != 1)
            {
                Debug.WriteLine("设置数据域波特率失败");
                return IntPtr.Zero;
            }

            // 终端电阻
            path = String.Format("{0}/initenal_resistance", chn_idx);
            string resistance = "1";//1-使能 0-禁能
            ret = ZLGCAN.ZCAN_SetValue(dev, path, resistance);
            if (ret != 1)
            {
                Debug.WriteLine("设置终端电阻失败");
                return IntPtr.Zero;
            }

            // 初始化通道
            ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG InitConfig = new ZLGCAN.ZCAN_CHANNEL_INIT_CONFIG(); // 结构体
            InitConfig.can_type = 1;                // 1 - CANFD (USBCANFD 只能选择CANFD模式)
            InitConfig.config.canfd.mode = 0;       // 0 - 正常模式，1 - 只听模式

            IntPtr P2InitConfig = Marshal.AllocHGlobal(Marshal.SizeOf(InitConfig));
            Marshal.StructureToPtr(InitConfig, P2InitConfig, true);                         // 转成指针
            temp = ZLGCAN.ZCAN_InitCAN(dev, (uint)chn_idx, P2InitConfig);
            Marshal.FreeHGlobal(P2InitConfig);                                              // 释放内存
            if (temp == IntPtr.Zero)
            {
                Debug.WriteLine("初始化通道失败");
                return IntPtr.Zero;
            }

            //// 设置滤波
            //value = "0";            // 0-标准帧 1-扩展帧
            // path = String.Format("{0}/filter_mode", chn_idx);
            // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value);

            // value = "0x731";        // 起始 ID
            // path = String.Format("{0}/filter_start", chn_idx);
            // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value);

            // value = "0x7DF";        // 结束 ID
            // path = String.Format("{0}/filter_end", chn_idx);
            // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value);

            // value = "0";            // 使能滤波
            // path = String.Format("{0}/filter_ack", chn_idx);
            // ret = ZLGCAN.ZCAN_SetValue(deviceHandle, path, value);
            // if (ret != 1)
            // {
            //     Debug.WriteLine("使能滤波失败");
            //     return IntPtr.Zero;
            // }

            //打开通道
            uint Ret = ZLGCAN.ZCAN_StartCAN(temp);
            if (Ret != 1)
            {
                Debug.WriteLine("打开通道失败");
                return IntPtr.Zero;
            }
            return temp;
        }

        // zlg接收线程
        private void ZlgReceiveThread(object obj)
        {
            ZlgThreadParams parameters = (ZlgThreadParams)obj;
            ZLGCAN.ZCAN_Receive_Data recData = new ZLGCAN.ZCAN_Receive_Data();
            ZLGCAN.ZCAN_ReceiveFD_Data recFDData = new ZLGCAN.ZCAN_ReceiveFD_Data();

            IntPtr P2zcanReceiveData = Marshal.AllocHGlobal(Marshal.SizeOf(recData) * 100);
            IntPtr P2zcanReceiveFDData = Marshal.AllocHGlobal(Marshal.SizeOf(recFDData) * 100);

            while (!parameters.CancellationToken.IsCancellationRequested)
            {
                uint RecNum = ZLGCAN.ZCAN_GetReceiveNum(parameters.chnHandle, 0);  // 0-CAN 1-CANFD 2-合并
                if (RecNum > 0)
                {
                    uint ReceiveNum = ZLGCAN.ZCAN_Receive(parameters.chnHandle, P2zcanReceiveData, 100, 0);
                    if (ReceiveNum > 0)
                    {
                        for (int i = 0; i < ReceiveNum; i++)
                        {
                            recData = (ZLGCAN.ZCAN_Receive_Data)Marshal.PtrToStructure(P2zcanReceiveData + i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_Receive_Data)), typeof(ZLGCAN.ZCAN_Receive_Data));
                            uint recCanID = recData.frame.can_id & 0x1FFFFFFF;
                            // ID过滤，非关注的ID丢弃
                            if (!_canFilter.IsInterested(recCanID))
                            {
                                continue;
                            }

                            int actualLength = recData.frame.can_dlc;
                            byte[] validData = new byte[actualLength];
                            Array.Copy(recData.frame.data, validData, actualLength);

                            _onReceiveCall.Invoke(recCanID, validData);

                            // 以下是调试信息，可以删除
                            //Debug.WriteLine("CHN:{0} [{1}] CAN  ", 0, recData.timestamp);

                            //// 帧类型
                            //if ((recData.frame.can_id & (1 << 31)) != 0)
                            //    Debug.Write("扩展帧 ");
                            //else
                            //    Debug.Write("标准帧 ");

                            //// 方向
                            //if ((recData.frame.__pad & 0x20) == 0x20)
                            //    Debug.Write("Tx ");
                            //else
                            //    Debug.Write("Rx ");

                            //// ID和数据
                            //Debug.WriteLine("ID:{0:X}  {1}", recData.frame.can_id & 0x1FFFFFFF, this.ByteArrayToHexString(recData.frame.data, recData.frame.can_dlc));
                        }
                    }
                }

                RecNum = ZLGCAN.ZCAN_GetReceiveNum(parameters.chnHandle, 1);   // CANFD
                if (RecNum > 0)
                {
                    uint ReceiveNum = ZLGCAN.ZCAN_ReceiveFD(parameters.chnHandle, P2zcanReceiveFDData, 100, 0);
                    if (ReceiveNum > 0)
                    {
                        for (int i = 0; i < ReceiveNum; i++)
                        {
                            recFDData = (ZLGCAN.ZCAN_ReceiveFD_Data)Marshal.PtrToStructure(P2zcanReceiveFDData + i * Marshal.SizeOf(typeof(ZLGCAN.ZCAN_ReceiveFD_Data)), typeof(ZLGCAN.ZCAN_ReceiveFD_Data));
                            // ID过滤，非关注的ID丢弃
                            uint recCanID = recFDData.frame.can_id & 0x1FFFFFFF;
                            // ID过滤，非关注的ID丢弃
                            if (!_canFilter.IsInterested(recCanID))
                            {
                                continue;
                            }
                            int actualLength = recFDData.frame.len;
                            byte[] validData = new byte[actualLength];
                            Array.Copy(recFDData.frame.data, validData, actualLength);

                            _onReceiveCall.Invoke(recCanID, validData);

                            //Debug.WriteLine("CHN:{0} [{1}] CANFD", 0, recFDData.timestamp);
                            //if ((recFDData.frame.flags & 0x01) == 0x01)
                            //    Debug.Write("加速");

                            //// 帧类型
                            //if ((recFDData.frame.can_id & (1 << 31)) != 0)
                            //    Debug.Write("  扩展帧");
                            //else
                            //    Debug.Write("  标准帧");

                            //// 方向
                            //if ((recFDData.frame.flags & 0x20) == 0x20)
                            //    Debug.Write(" Tx ");
                            //else
                            //    Debug.Write(" Rx ");

                            //// ID和数据
                            //Debug.WriteLine("ID:{0:X}  {1}", recFDData.frame.can_id & 0x1FFFFFFF, this.ByteArrayToHexString(recFDData.frame.data, recFDData.frame.len));
                        }
                    }
                }
                Thread.Sleep(1);
            }
            Debug.WriteLine("ZLG监听进程结束");
            Marshal.FreeHGlobal(P2zcanReceiveData);
            Marshal.FreeHGlobal(P2zcanReceiveFDData);
        }

        // CAN发送方法
        private bool ZlgCAN_Transmit(uint id, byte[] data)
        {
            bool ret = true;
            // 构造 can_frame
            var frame = new ZLGCAN.can_frame
            {
                can_id = id,
                can_dlc = 8,// 发送长度固定为8
                data = new byte[8]
            };

            Array.Copy(data, frame.data, data.Length);

            // 构造 ZCAN_Transmit_Data
            var txData = new ZLGCAN.ZCAN_Transmit_Data
            {
                transmit_type = 0, // 正常发送
                frame = frame
            };

            // 分配非托管内存
            int structSize = Marshal.SizeOf<ZLGCAN.ZCAN_Transmit_Data>();// 计算需要分配内存大小
            IntPtr pTxData = Marshal.AllocHGlobal(structSize);

            // 托管内存拷贝至非托管内存
            Marshal.StructureToPtr(txData, pTxData, false);

            // zlg发送
            uint transmitCnt = ZLGCAN.ZCAN_Transmit(_chnHandle, pTxData, 1);
            // 发送失败3次重发
            if (transmitCnt < 1)// 返回的是实际发送报文的数量
            {
                int sleepTime = 3;
                for (int i = 0; i < 3; i++)
                {
                    transmitCnt = ZLGCAN.ZCAN_Transmit(_chnHandle, pTxData, 1);
                    if (transmitCnt == 1)// 发送成功
                    {
                        break;
                    }
                    Thread.Sleep(sleepTime);
                    sleepTime += 2;
                }
                if (sleepTime == 9)
                {
                    ret = false;
                    Debug.WriteLine("发送失败");
                }
            }

            // 释放内存
            Marshal.FreeHGlobal(pTxData);

            return ret;
        }

        // CANFD发送方法
        private bool ZlgCANFD_Transmit(uint id, byte[] data)
        {
            bool ret = true;

            byte framBrs = (byte)((_isCanFDSend == ZlgCanDevice.CanSendMode.CANFD) ? 0 : 1);

            // 构造 canfd_frame
            var frame = new ZLGCAN.canfd_frame
            {
                can_id = id,
                flags = framBrs,
                len = (byte)data.Length,
                data = new byte[64]
            };

            Array.Copy(data, frame.data, frame.len);

            // 构造 ZCAN_Transmit_Data
            var txData = new ZLGCAN.ZCAN_TransmitFD_Data
            {
                transmit_type = 0, // 正常发送
                frame = frame
            };

            // 分配非托管内存
            int structSize = Marshal.SizeOf<ZLGCAN.ZCAN_TransmitFD_Data>();// 计算需要分配内存大小
            IntPtr pTxData = Marshal.AllocHGlobal(structSize);

            // 托管内存拷贝至非托管内存
            Marshal.StructureToPtr(txData, pTxData, false);

            // zlg发送
            uint transmitCnt = ZLGCAN.ZCAN_TransmitFD(_chnHandle, pTxData, 1);
            // 发送失败3次重发
            if (transmitCnt < 1)// 返回的是实际发送报文的数量
            {
                int sleepTime = 3;
                for (int i = 0; i < 3; i++)
                {
                    transmitCnt = ZLGCAN.ZCAN_TransmitFD(_chnHandle, pTxData, 1);
                    if (transmitCnt == 1)// 发送成功
                    {
                        break;
                    }
                    Thread.Sleep(sleepTime);
                    sleepTime += 2;
                }
                if (sleepTime == 9)
                {
                    ret = false;
                    Debug.WriteLine("发送失败");
                }
            }

            // 释放内存
            Marshal.FreeHGlobal(pTxData);

            return ret;
        }

        // 临时用下
        private string ByteArrayToHexString(byte[] bytes, byte len)
        {
            if (bytes == null || len == 0) return "";

            int actualLen = Math.Min(len, bytes.Length);
            var sb = new StringBuilder(actualLen * 3); // 每字节 "XX " 共3字符

            for (int i = 0; i < actualLen; i++)
            {
                sb.Append(bytes[i].ToString("X2"));
                sb.Append(' ');
            }

            return sb.ToString();
        }
    }
}
